﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.IO;
using Pixysoft.Framework.Configurations.Core;

namespace Pixysoft.Framework.Configurations.Controller
{
    /// <summary>
    /// 读取部分
    /// </summary>
    partial class ConfigurationController
    {
        private static volatile ConfigurationController instance;

        private static object syncRoot = new Object();

        public static ConfigurationController Instance
        {
            get
            {
                if (instance == null)
                {
                    lock (syncRoot)
                    {
                        instance = new ConfigurationController();
                    }
                }

                return instance;

            }
        }

        /// <summary>
        /// 加载全部configuration到内存
        /// </summary>
        /// <param name="filepath"></param>
        /// <returns></returns>
        public IConfigurationCollection GetConfiguration(string filename)
        {
            IConfigurationCollection collection = null;

            if (!File.Exists(filename))
                return null;

            using (Stream stream = File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
            {
                XmlReader reader = XmlReader.Create(stream);

                collection = new XmlReaderStartState(reader).Read();

                reader.Close();
            }

            return collection;
        }

        /// <summary>
        /// 加载指定节点的configuration到内存 nameValue必须为空 否则加载失败
        /// </summary>
        /// <param name="filepath"></param>
        /// <param name="name"></param>
        /// <returns></returns>
        public IConfiguration GetConfiguration(string filename, string idValue)
        {
            return GetConfiguration(filename, idValue, null);
        }

        /// <summary>
        /// 加载指定节点的configuration到内存
        /// </summary>
        /// <param name="filepath"></param>
        /// <param name="name"></param>
        /// <returns></returns>
        public IConfiguration GetConfiguration(string filename, string idValue, string nameValue)
        {
            IConfiguration configuration = null;

            if (!File.Exists(filename))
                return null;

            using (Stream stream = File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
            {
                //10-02-15 可以强迫设置encoding 否则遇到中文会出错（当经过vs2005的处理后，有时候中文编程其他编码）

                //XmlReader reader = XmlReader.Create(new StreamReader(stream, Encoding.GetEncoding("gb2312")));

                XmlReader reader = XmlReader.Create(stream);

                configuration = new XmlReaderSubStartState(reader).Read(idValue, nameValue);

                reader.Close();
            }

            return configuration;
        }


        /// <summary>
        /// 反序列化配置
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public IConfigurationCollection DeserializeConfigurations(string value)
        {
            IConfigurationCollection collection = null;

            using (Stream stream = Pixysoft.StreamCoder.Instance.ToStream(value))
            {
                XmlReader reader = XmlReader.Create(stream);

                collection = new XmlReaderStartState(reader).Read();

                reader.Close();
            }

            return collection;
        }

        /// <summary>
        /// 反序列化配置
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public IConfiguration DeserializeConfiguration(string value, string idValue, string nameValue)
        {
            IConfiguration configuration = null;

            using (Stream stream = Pixysoft.StreamCoder.Instance.ToStream(value))
            {
                XmlReader reader = XmlReader.Create(stream);

                configuration = new XmlReaderSubStartState(reader).Read(idValue, nameValue);

                reader.Close();
            }

            return configuration;
        }


        /// <summary>
        /// 从指定节点开始寻找
        /// </summary>
        class XmlReaderSubStartState
        {
            XmlReader reader = null;

            public XmlReaderSubStartState(XmlReader reader)
            {
                this.reader = reader;
            }

            public IConfiguration Read(string idValue, string nameValue)
            {
                while (reader.Read())
                {
                    switch (reader.NodeType)
                    {
                        case XmlNodeType.Element:
                            {
                                string elementname = reader.Name;

                                if (!string.Equals(elementname, StringHelper.default_element_config, StringComparison.OrdinalIgnoreCase))
                                    break;

                                if (!reader.MoveToAttribute(StringHelper.default_attribute_id))
                                    break;

                                string tidvalue = reader.Value;

                                if (string.IsNullOrEmpty(tidvalue))
                                    break;

                                tidvalue = tidvalue.Trim();

                                if (!string.Equals(tidvalue, idValue, StringComparison.OrdinalIgnoreCase))
                                    break;


                                if (!string.IsNullOrEmpty(nameValue))
                                {
                                    if (!reader.MoveToAttribute(StringHelper.default_attribute_name))
                                        break;

                                    string tnamevalue = reader.Value;

                                    if (string.IsNullOrEmpty(tnamevalue))
                                        break;

                                    tnamevalue = tnamevalue.Trim();

                                    if (!string.Equals(tnamevalue, nameValue, StringComparison.OrdinalIgnoreCase))
                                        break;
                                }
                                else
                                {
                                    //10-01-05 如果没有要求 则同样要核对是否不存在对应的attribute

                                    if (!reader.MoveToAttribute(StringHelper.default_attribute_name))
                                    {
                                        //correct
                                    }
                                    else
                                    {
                                        string tnamevalue = reader.Value;

                                        if (!string.IsNullOrEmpty(tnamevalue))
                                            break;
                                    }
                                }

                                reader.MoveToElement();

                                ConfigurationSetting setting = new XmlReaderConfigState(reader).Read();

                                return setting;
                            }
                        default:
                            {
                                break;
                            }
                    }
                }

                return null;
            }
        }

        /// <summary>
        /// 主要负责 <config id=xxx> 开始标签的处理 如果没有id，则忽略
        /// </summary>
        class XmlReaderStartState
        {
            XmlReader reader = null;

            public XmlReaderStartState(XmlReader reader)
            {
                this.reader = reader;
            }

            public IConfigurationCollection Read()
            {
                ConfigurationSettingCollection collection = new ConfigurationSettingCollection();

                while (reader.Read())
                {
                    switch (reader.NodeType)
                    {
                        case XmlNodeType.Element:
                            {
                                string elementname = reader.Name;

                                if (!string.Equals(elementname, StringHelper.default_element_config, StringComparison.OrdinalIgnoreCase))
                                    break;

                                if (!reader.MoveToAttribute(StringHelper.default_attribute_id))
                                    break;

                                reader.MoveToElement();

                                ConfigurationSetting setting = new XmlReaderConfigState(reader).Read();

                                collection.Add(setting);

                                break;
                            }
                        default:
                            {
                                break;
                            }
                    }
                }

                return collection;
            }
        }

        /// <summary>
        /// 负责<config> 内部节点的初始化 读到</config>结束
        /// </summary>
        class XmlReaderConfigState
        {
            XmlReader reader = null;

            public XmlReaderConfigState(XmlReader reader)
            {
                this.reader = reader;
            }

            internal ConfigurationSetting Read()
            {
                //首先初始化id

                if (!reader.MoveToAttribute(StringHelper.default_attribute_id))
                    throw new Exception("missing id attribute in config element, please check config files.");

                string idvalue = reader.Value;

                if (string.IsNullOrEmpty(idvalue))
                    throw new Exception("missing id value in id attribute, config element, please check config file.");

                idvalue = idvalue.Trim();

                string namevalue = null;

                if (reader.MoveToAttribute(StringHelper.default_attribute_name))
                    namevalue = reader.Value;

                if (!string.IsNullOrEmpty(namevalue))
                    namevalue = namevalue.Trim();

                ConfigurationSetting setting = new ConfigurationSetting(idvalue, namevalue);

                if (reader.IsEmptyElement)
                    return setting;


                //然后初始化内部节点

                while (reader.Read())
                {
                    switch (reader.NodeType)
                    {
                        case XmlNodeType.Element:
                            {
                                setting.Nodes.Add(new XmlReaderNodeState(reader).Read());

                                //09-12-01 遗留的bug

                                //if (reader.IsEmptyElement)
                                //    return setting;

                                break;
                            }
                        case XmlNodeType.EndElement:
                            {
                                if (string.Equals(reader.Name, StringHelper.default_element_config, StringComparison.OrdinalIgnoreCase))
                                    return setting;

                                break;
                            }
                        default:
                            {
                                break;
                            }
                    }
                }

                throw new Exception("missing end config tag in configuration, id = " + idvalue);
            }
        }

        /// <summary>
        /// 主要负责 节点的属性初始化、同时递归节点的子节点初始化
        /// </summary>
        class XmlReaderNodeState
        {
            XmlReader reader = null;

            public XmlReaderNodeState(XmlReader reader)
            {
                this.reader = reader;
            }

            internal IConfigurationNode Read()
            {
                //初始化节点名称

                string nodename = reader.Name;

                if (string.IsNullOrEmpty(nodename))
                    throw new Exception("missing nodename in node, please check config.");

                //09-11-25 区分大小写

                nodename = nodename.Trim();

                IConfigurationNode node = new ConfigurationNode(nodename);

                if (reader.HasAttributes)
                {
                    for (int i = 0; i < reader.AttributeCount; i++)
                    {
                        reader.MoveToAttribute(i);

                        string attributename = reader.Name;

                        if (string.IsNullOrEmpty(attributename))
                            continue;

                        attributename = attributename.Trim();

                        string attributevalue = reader.Value;

                        if (string.IsNullOrEmpty(attributevalue))
                            attributevalue = "";
                        else
                            attributevalue.Trim();

                        node.Property.Add(attributename, attributevalue);
                    }
                }

                //09-11-24 如果没有标签 返回false 但是不影响操作

                reader.MoveToElement();

                if (reader.IsEmptyElement)
                    return node;

                while (reader.Read())
                {
                    switch (reader.NodeType)
                    {
                        case XmlNodeType.Text:
                            {
                                string value = reader.Value;

                                if (string.IsNullOrEmpty(value))
                                    value = "";

                                //不能用toupper

                                node.Text = value.Trim();

                                break;
                            }
                        case XmlNodeType.Element:
                            {
                                node.Nodes.Add(new XmlReaderNodeState(reader).Read());

                                //if (reader.IsEmptyElement)
                                //    return node;

                                break;
                            }
                        case XmlNodeType.EndElement:
                            {
                                if (string.Equals(nodename, reader.Name, StringComparison.OrdinalIgnoreCase))
                                    return node;

                                break;
                            }
                        default:
                            {
                                break;
                            }

                    }
                }

                throw new Exception("missing end element tag for node " + nodename);
            }
        }
    }

    /// <summary>
    /// 写入部分
    /// </summary>
    partial class ConfigurationController
    {
        public string Serialize(IConfiguration config, bool asObject)
        {
            return Serialize(config, asObject, false);
        }

        public string Serialize(IConfiguration config, bool asObject, bool indent)
        {
            StringWriter stringWriter = new StringWriter();

            XmlWriterSettings setting = new XmlWriterSettings();

            setting.Indent = indent;

            setting.OmitXmlDeclaration = true;

            XmlWriter writer = XmlWriter.Create(stringWriter, setting);

            Serialize(writer, config, asObject);

            writer.Flush();

            writer.Close();

            return stringWriter.ToString();
        }

        public void Save(string filename, IConfiguration config, bool asObject, bool indent)
        {
            using (Stream stream = File.Open(filename, FileMode.Create))
            {
                XmlWriterSettings setting = new XmlWriterSettings();

                setting.Indent = indent;

                setting.OmitXmlDeclaration = true;

                XmlWriter writer = XmlWriter.Create(stream, setting);

                Serialize(writer, config, asObject);

                writer.Flush();

                writer.Close();

                stream.Close();

                stream.Dispose();
            }
        }

        private void Serialize(XmlWriter writer, IConfiguration config, bool asObject)
        {
            //writer.WriteStartDocument();

            //09-11-25 bug 就算手动设置setting仍然不行 因此要覆盖

            writer.WriteProcessingInstruction("xml", "version=\"1.0\" encoding=\"utf-8\"");

            writer.WriteStartElement(GetStartupElement(config, asObject));

            //10-01-09 漏了name

            if (!asObject)
                writer.WriteAttributeString(StringHelper.default_attribute_id, config.Id);

            if (!string.IsNullOrEmpty(config.Name))
                writer.WriteAttributeString(StringHelper.default_attribute_name, config.Name);

            foreach (IConfigurationNode node in config.Node)
            {
                SerializeNode(writer, node);
            }

            writer.WriteEndElement();

            writer.Close();
        }

        private void SerializeNode(XmlWriter writer, IConfigurationNode node)
        {
            writer.WriteStartElement(node.NodeName);

            foreach (IConfigurationProperty property in node.Property)
            {
                writer.WriteAttributeString(property.Key, property.Value);
            }

            foreach (IConfigurationNode subnode in node.Node)
            {
                SerializeNode(writer, subnode);
            }

            writer.WriteString(node.Text);

            writer.WriteEndElement();
        }

        private string GetStartupElement(IConfiguration config, bool asObject)
        {
            if (asObject)
                return config.Id;

            return StringHelper.default_element_config;
        }
    }
}
